import sys
import csv
from datetime import datetime
import serial
import serial.tools.list_ports
from PyQt5.QtCore import QTimer, Qt
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, 
                            QHBoxLayout, QLabel, QComboBox, QPushButton, QStatusBar)
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from matplotlib.figure import Figure
from matplotlib import pyplot as plt
plt.rcParams['font.family'] = 'SimHei'  # 替换为你选择的字体

class SensorGUI(QMainWindow):
    def __init__(self):
        super().__init__()
        self.serial = None
        self.timer = QTimer()  # 先创建timer
        self.data = {'time': [], 'temp': [], 'humi': []}
        self.max_points = 50
        self.initUI()  # 再初始化UI

    def initUI(self):
        self.setWindowTitle('温湿度监测系统 (Matplotlib版)')
        self.setGeometry(100, 100, 800, 600)

        # 主控件
        main_widget = QWidget()
        self.setCentralWidget(main_widget)
        layout = QVBoxLayout(main_widget)

        # 串口配置区域
        port_layout = QHBoxLayout()
        self.port_combo = QComboBox()
        self.baud_combo = QComboBox()
        self.baud_combo.addItems(['9600', '115200', '57600', '38400'])
        self.connect_btn = QPushButton('打开串口')
        self.connect_btn.clicked.connect(self.toggle_serial)

        port_layout.addWidget(QLabel('端口:'))
        port_layout.addWidget(self.port_combo)
        port_layout.addWidget(QLabel('波特率:'))
        port_layout.addWidget(self.baud_combo)
        port_layout.addWidget(self.connect_btn)
        layout.addLayout(port_layout)

        # 实时数据显示
        data_layout = QHBoxLayout()
        self.temp_label = QLabel('温度: --℃')
        self.humi_label = QLabel('湿度: --%')
        self.temp_label.setStyleSheet('font-size: 24px; color: red;')
        self.humi_label.setStyleSheet('font-size: 24px; color: blue;')
        data_layout.addWidget(self.temp_label)
        data_layout.addWidget(self.humi_label)
        layout.addLayout(data_layout)

        # Matplotlib图表
        self.figure = Figure(figsize=(8, 4), dpi=100)
        self.canvas = FigureCanvas(self.figure)
        self.ax = self.figure.add_subplot(111)
        self.setup_chart()
        layout.addWidget(self.canvas)

        # 状态栏
        self.status_bar = QStatusBar()
        self.setStatusBar(self.status_bar)

        # 初始化
        self.scan_ports()
        self.timer.timeout.connect(self.read_data)
        self.show()

    def setup_chart(self):
        self.ax.set_title('温湿度实时数据')
        self.ax.set_xlabel('时间')
        self.ax.set_ylabel('数值')
        self.ax.set_ylim(0, 100,auto=True)
        self.ax.grid(True)
        
        # 初始化曲线
        self.line_temp, = self.ax.plot([], [], 'r-', marker='o',label='温度')
        self.line_humi, = self.ax.plot([], [], 'b-', marker='o',label='湿度')
        self.ax.legend(loc='upper left')

    def update_chart(self):
        # 更新曲线数据
        self.line_temp.set_data(range(len(self.data['temp'])), self.data['temp'])
        self.line_humi.set_data(range(len(self.data['humi'])), self.data['humi'])
        
        # 自动调整X轴范围
        x_max = max(len(self.data['temp'])-1, 0)
        self.ax.set_xlim(max(0, x_max - self.max_points), x_max + 1)
        
        # 重绘图表
        self.canvas.draw_idle()

    def scan_ports(self):
        self.port_combo.clear()
        ports = serial.tools.list_ports.comports()
        for port in ports:
            self.port_combo.addItem(port.device)

    def toggle_serial(self):
        if self.serial and self.serial.is_open:
            self.close_serial()
        else:
            self.open_serial()

    def open_serial(self):
        port = self.port_combo.currentText()
        baud = int(self.baud_combo.currentText())
        try:
            self.serial = serial.Serial(port, baud, timeout=1)
            self.timer.start(1000)
            self.connect_btn.setText('关闭串口')
            self.status_bar.showMessage(f'已连接 {port}@{baud}')
        except Exception as e:
            self.status_bar.showMessage(f'连接失败: {str(e)}')

    def close_serial(self):
        if self.serial:
            self.timer.stop()
            self.serial.close()
        self.connect_btn.setText('打开串口')
        self.status_bar.showMessage('串口已关闭')

    def read_data(self):
        if self.serial and self.serial.in_waiting:
            try:
                raw_data = self.serial.readline().decode().strip()
                temp, humi = self.parse_data(raw_data)
                self.update_display(temp, humi)
                self.save_to_csv(temp, humi)
                self.status_bar.showMessage(f'已接收数据T={temp},H={humi}') 
            except Exception as e:
                self.status_bar.showMessage(f'数据解析错误: {str(e)}')

    def parse_data(self, raw):
        # 示例数据格式: "T=25.50,H=60.00"
        temp_str = raw.split(',')[0].split('T=')[1]
        humi_str = raw.split(',')[1].split('H=')[1]
        return float(temp_str), float(humi_str)

    def update_display(self, temp, humi):
        # 更新标签
        self.temp_label.setText(f'温度: {temp:.1f}℃')
        self.humi_label.setText(f'湿度: {humi:.1f}%')

        # 更新数据存储
        self.data['temp'].append(temp)
        self.data['humi'].append(humi)
        
        # 保持最大数据点数
        if len(self.data['temp']) > self.max_points:
            self.data['temp'].pop(0)
            self.data['humi'].pop(0)

        # 更新图表
        self.update_chart()

    def save_to_csv(self, temp, humi):
        timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        with open('sensor_data.csv', 'a', newline='') as f:
            writer = csv.writer(f)
            writer.writerow([timestamp, temp, humi])

    def closeEvent(self, event):
        self.close_serial()
        event.accept()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = SensorGUI()
    sys.exit(app.exec_())